/**
 * FtpConnector.java
 * @author Daniel Liu
 * Create: 2006-12-22 ����07:14:28
 */
package org.lex.net.ftp.util;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Timer;
import java.util.TimerTask;

import org.lex.net.Password;
import org.lex.net.event.ProtocolListener;
import org.lex.net.ftp.FTPURL;
import org.lex.net.ftp.IFTPClient;
import org.lex.net.log.EnhancedLog;
import org.lex.net.log.Log;
import org.lex.net.log.NullLog;
import org.lex.utils.StateChangeSupport;


public class FTPConnector extends StateChangeSupport {
	private int retryDelay = 15000;
	private int maxRetryCount = 100;
	// =============================
	private EnhancedLog log = null;
	private FTPClientFactory factory = null;
	private ProtocolListener ftpListener = null;
	private IFTPClient ftpClient = null;
	private FTPURL url = null;
	// =============================
	private Timer timer = null;
	private int retryCount = 0;
	private boolean cancel = false;

	public FTPConnector(Log log) {
		this(log, null);
	}

	public FTPConnector(Log log, FTPClientFactory factory) {
		super();
		if (null == log)
			log = new NullLog();
		if (null == factory)
			factory = FTPClientFactory.getDefaultFactory();
		this.factory = factory;
		this.log = new EnhancedLog(log);
		this.ftpListener = new LogListener(log);
	}

	public IFTPClient getFtpClient() {
		return this.ftpClient;
	}

	public boolean isConnected() {
		return this.isConnected(this.ftpClient);
	}

	private boolean isConnected(IFTPClient ftp) {
		if (null == ftp)
			return false;
		return ftp.isServerOpen();
	}

	public void connect(FTPURL url) {
		this.url = url;
		this.cancel = false;
		this.retryCount = 0;
		this.log.setSource(url);
		this.timer = new Timer(true);
		this.timer.schedule(new Connector(), 0, retryDelay);
	}

	public void cancelRetry() {
		this.cancel = true;
		if (null != timer) {
			this.timer.cancel();
			this.timer = null;
		}
	}

	public void close() {
		this.cancelRetry();
		if (null != this.ftpClient)
			factory.close(ftpClient);
	}

	public int getRetryCount() {
		return this.retryCount;
	}

	class Connector extends TimerTask {
		public void run() {
			// check if reconnect was cancelled
			if (true == cancel) {
				log.error("Reconnect attempt was cancelled");
				cancelRetry();
				fireStateChanged(FTPConnector.this);
				return;
			}
			IFTPClient ftp = factory.newFTPClient();
			ftp.setPassiveMode(true);
			ftp.setServerCharset(Charset.defaultCharset());
			ftp.addProtocolListener(ftpListener);
			this.tryToConnect(ftp);
			// check if already connected
			if (true == isConnected(ftp)) {
				FTPConnector.this.ftpClient = ftp;
				cancelRetry();
				fireStateChanged(FTPConnector.this);
				return;
			}
			// not connected, wait for (#retryDelay) to reconnect
			if (false == isConnected(ftp)) {
				retryCount++;
				// check if reach the max-retry-count
				if (retryCount > maxRetryCount) {
					log.error("Reach the maximum reconnect count and abort");
					cancelRetry();
					fireStateChanged(FTPConnector.this);
					return;
				}
				log.misc("Delaying for " + (retryDelay / 1000L) + " seconds before reconnect attempt #" + retryCount);
			}
			ftp.removeProtocolListener(ftpListener);
			factory.close(ftp);
		}

		private void tryToConnect(IFTPClient ftp) {
			try {
				// try to connect to server
				log.misc("Connecting to " + url.getHost() + ":" + url.getPort());
				ftp.openServer(url.getHost(), url.getPort());
			} catch (IOException e) {
				log.error("Connection Failed");
				log.error(e.getMessage());
				e.printStackTrace();
				return;
			}
			try {
				// try to login
				Password auth = url.getAuthentication();
				ftp.login(auth.getUserName(), auth.getPassword());
			} catch (IOException e) {
				log.error("Login failed");
				log.error(e.getMessage());
				return;
			}
			try {
				ftp.feature();
			} catch (IOException e) {
				log.error(e.getMessage());
				return;
			}
		}
	}
}
